/*	$Id: if_atm.c 6 2008-07-29 02:30:10Z phrakt $	*/
/*
 * Copyright (c) 2003 Jean-Francois Brousseau <jfb@openbsd.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/param.h>
#include <sys/stat.h>
#include <sys/ioctl.h>

#if defined(__OpenBSD__) || defined(__NetBSD__)
#include <net/if_dl.h>
#include <net/if_types.h>

#include <ifaddrs.h>

#elif defined(__linux__)
#include <net/if_arp.h>

/* stupid linux defines endianness-swapping functions in netinet code */
#include <netinet/in.h>
#endif

#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include "if.h"
#include "dlink.h"
#include "pfnet.h"
#include "pforge.h"


#define PFIF_ETHER_ADDRLEN       6     /* address length in bytes */
#define PFIF_ETHER_TYPELEN       2     /* EtherType length */
#define PFIF_ETHER_CRCLEN        4     /* Cyclic Redundancy Check length */
#define PFIF_ETHER_MINFRMLEN    64     /* minimum frame length */
#define PFIF_ETHER_MAXFRMLEN  1518     /* max frame length */

#define PFIF_ETHER_HDRLEN ((PFIF_ETHER_ADDRLEN * 2) + PFIF_ETHER_TYPELEN)

#define PFIF_ETHER_MAXMTU \
	(PFIF_ETHER_MAXFRMLEN - PFIF_ETHER_HDRLEN - PFIF_ETHER_CRCLEN)
#define PFIF_ETHER_MINMTU \
	(PFIF_ETHER_MINFRMLEN - PFIF_ETHER_HDRLEN - PFIF_ETHER_CRCLEN)

#define PFIF_ETHER_ADDRFMT   "%02x:%02x:%02x:%02x:%02x:%02x"


/* atmnet types */
#define PFIF_ETHERTYPE_PUP    0x0200
#define PFIF_ETHERTYPE_IP     0x0800    /* Internet protocol */
#define PFIF_ETHERTYPE_ARP    0x0806    /* address resolution protocol */
#define PFIF_ETHERTYPE_RARP   0x8035    /* reverse ARP */
#define PFIF_ETHERTYPE_IP6    0x86dd    /* IP version 6 */



int   pfif_atm_init    (void);
void  pfif_atm_fini    (void);
void  pfif_atm_input   (struct pfnet_if *);
void  pfif_atm_output  (struct pfnet_if *);
void  pfif_atm_print   (struct pfpkt *);





struct pfif_atmhdr {
	uint8_t   eh_dst[PFIF_ETHER_ADDRLEN];
	uint8_t   eh_src[PFIF_ETHER_ADDRLEN];
	uint16_t  eh_type;
};



struct pfnet_if_ops pfif_atm_ops = {
	pfif_atm_init,
	pfif_atm_fini,
	pfif_atm_input,
	pfif_atm_output,
	pfif_atm_print
};





/*
* pfif_atm_init()
*
* Initialization routine for the ATM
*/

int
pfif_atm_init(void)
{
	return (0);
}


/*
* pfif_atm_fini()
*
*/

void
pfif_atm_fini(void)
{

}


/*
* pfif_atm_input()
*
* Input routine to read ATM frames and decapsulate the ATM layer.
*/

void
pfif_atm_input(struct pfnet_if *ifp)
{
	int ret;
	uint8_t bigbuf[PFIF_ETHER_MAXFRMLEN];
	struct pfpkt *pkt;
	struct pfif_atmhdr *hdr;

	pkt = pfnet_pkt_alloc();
	if (pkt == NULL) {
		pfnet_report(REP_ERR, "failed to allocate packet");
		return;
	}

	ret = dlink_read(ifp->ifdl, bigbuf, sizeof(bigbuf));
	if (ret < 0) {
		pfnet_report(REP_ERR, "failed to read data");
	}

	hdr = (struct pfif_atmhdr *)bigbuf;
	pfif_atm_print(hdr);
}


/*
* pfif_atm_output()
*
*/

void
pfif_atm_output(struct pfnet_if *ifp)
{
	uint8_t bigbuf[2048];
	struct pfif_atmhdr *hdr;

#if 0
	if (flen < PFIF_ETHER_MINFRMLEN) {
		/* pad with 0's */
	}
#endif

	if (dlink_write(ifp->ifdl, bigbuf, sizeof(bigbuf)) < 0) {
		pfnet_report(REP_ERR, "failed to read data");
	}

	hdr = (struct pfif_atmhdr *)bigbuf;
}


/*
* pfif_atm_print()
*
* Print a human-readable version of the data contained in an ATM header.
*/

void
pfif_atm_print(struct pfpkt *pkt)
{
	struct pfif_atmhdr *hdr;


	printf("Dst: " PFIF_ETHER_ADDRFMT,
	    hdr->eh_dst[0], hdr->eh_dst[1], hdr->eh_dst[2],
	    hdr->eh_dst[3], hdr->eh_dst[4], hdr->eh_dst[5]);

	printf("   Src: " PFIF_ETHER_ADDRFMT,
	    hdr->eh_src[0], hdr->eh_src[1], hdr->eh_src[2],
	    hdr->eh_src[3], hdr->eh_src[4], hdr->eh_src[5]);

	printf("   Type: 0x%x\n", ntohs(hdr->eh_type));
}
